package com.school.sports.exception;

import com.school.sports.common.Result;
import com.school.sports.entity.ExceptionRecord;
import com.school.sports.service.ExceptionHandlingService;
import com.school.sports.service.ExceptionNotificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.security.auth.message.AuthException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 全局异常处理器（增强版）
 * 提供异常自动记录、分类、级别判定和通知功能
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Autowired
    private ExceptionHandlingService exceptionHandlingService;

    @Autowired
    private ExceptionNotificationService exceptionNotificationService;

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.error("业务异常：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "业务异常");

        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数校验异常（@RequestBody）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        log.error("参数校验失败：{}", message);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "参数校验异常");

        return Result.paramError(message);
    }

    /**
     * 处理参数校验异常（@ModelAttribute）
     */
    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException e, HttpServletRequest request) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        log.error("参数绑定失败：{}", message);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "参数绑定异常");

        return Result.paramError(message);
    }

    /**
     * 处理参数校验异常（@Validated）
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<?> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        String message = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining("; "));
        log.error("参数约束违反：{}", message);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "参数约束异常");

        return Result.paramError(message);
    }

    /**
     * 处理数据库唯一约束异常
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<?> handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e, HttpServletRequest request) {
        log.error("数据库约束违反：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "数据库约束异常");

        String message = e.getMessage();
        if (message != null && message.contains("Duplicate entry")) {
            try {
                String[] split = message.split(" ");
                if (split.length > 2) {
                    String msg = split[2] + "已存在";
                    return Result.error(msg);
                }
            } catch (Exception ex) {
                log.warn("解析数据库异常消息失败：{}", ex.getMessage());
            }
        }
        return Result.error("数据库操作失败，请检查数据完整性");
    }

    /**
     * 处理认证异常
     */
    @ExceptionHandler({AuthException.class, org.springframework.security.authentication.AuthenticationCredentialsNotFoundException.class})
    public Result<?> handleAuthenticationException(Exception e, HttpServletRequest request) {
        log.error("认证异常：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "认证异常");

        return Result.unauthorized("认证失败，请重新登录");
    }

    /**
     * 处理授权异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public Result<?> handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
        log.error("授权异常：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "授权异常");

        return Result.forbidden("权限不足，无法访问该资源");
    }

    /**
     * 处理运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result<?> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        log.error("运行时异常：", e);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "运行时异常");

        return Result.error("系统运行异常，请稍后重试");
    }

    /**
     * 处理空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<?> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
        log.error("空指针异常：", e);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "空指针异常");

        return Result.error("系统内部错误，请联系管理员");
    }

    /**
     * 处理数字格式异常
     */
    @ExceptionHandler(NumberFormatException.class)
    public Result<?> handleNumberFormatException(NumberFormatException e, HttpServletRequest request) {
        log.error("数字格式异常：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "数字格式异常");

        return Result.paramError("数字格式错误");
    }

    /**
     * 处理非法参数异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
        log.error("非法参数异常：{}", e.getMessage());

        // 异步记录异常信息
        recordExceptionAsync(e, request, "非法参数异常");

        return Result.paramError(e.getMessage());
    }

    /**
     * 处理其他未知异常
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e, HttpServletRequest request) {
        log.error("系统异常：", e);

        // 异步记录异常信息
        recordExceptionAsync(e, request, "系统异常");

        return Result.error("系统繁忙，请稍后重试");
    }

    /**
     * 异步记录异常信息并发送通知
     * @param exception 异常对象
     * @param request 请求对象
     * @param exceptionType 异常类型描述
     */
    private void recordExceptionAsync(Exception exception, HttpServletRequest request, String exceptionType) {
        try {
            // 获取当前用户信息
            final Long operatorId;
            final String operatorName;

            Long tempOperatorId = null;
            String tempOperatorName = "系统";

            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal())) {
                // 这里需要根据实际的用户实体来获取用户信息
                // 假设用户信息存储在Authentication的principal中
                Object principal = authentication.getPrincipal();
                if (principal instanceof org.springframework.security.core.userdetails.UserDetails) {
                    String username = ((org.springframework.security.core.userdetails.UserDetails) principal).getUsername();
                    tempOperatorName = username;
                    // 这里可以通过用户名查询用户ID，需要注入UserService
                    // tempOperatorId = userService.findByUsername(username).getId();
                }
            }

            operatorId = tempOperatorId;
            operatorName = tempOperatorName;

            // 异步记录异常，避免影响正常响应
            CompletableFuture.runAsync(() -> {
                try {
                    // 记录异常信息
                    boolean recordSuccess = exceptionHandlingService.recordException(exceptionType,
                            exception.getMessage(),
                            request != null ? request.getRequestURI() : "未知位置",
                            exceptionHandlingService.determineExceptionLevel(exception, exceptionType),
                            getStackTrace(exception),
                            request,
                            operatorId,
                            operatorName);

                    // 如果记录成功，发送通知
                    if (recordSuccess) {
                        try {
                            // 判定是否为严重异常
                            Integer exceptionLevel = exceptionHandlingService.determineExceptionLevel(exception, exceptionType);
                            boolean isSevere = ExceptionRecord.Level.SEVERE.getCode().equals(exceptionLevel);

                            if (isSevere) {
                                // 发送严重异常通知
                                exceptionNotificationService.sendSevereExceptionNotification(null);
                            } else {
                                // 发送普通异常通知
                                exceptionNotificationService.sendExceptionNotification(null);
                            }
                        } catch (Exception notificationException) {
                            log.error("发送异常通知失败：{}", notificationException.getMessage(), notificationException);
                        }
                    }
                } catch (Exception recordException) {
                    log.error("记录异常信息失败：{}", recordException.getMessage(), recordException);
                }
            });
        } catch (Exception ex) {
            log.error("异步记录异常信息失败：{}", ex.getMessage(), ex);
        }
    }

    /**
     * 获取异常堆栈信息
     * @param exception 异常对象
     * @return 堆栈信息字符串
     */
    private String getStackTrace(Exception exception) {
        try {
            java.io.StringWriter sw = new java.io.StringWriter();
            java.io.PrintWriter pw = new java.io.PrintWriter(sw);
            exception.printStackTrace(pw);
            return sw.toString();
        } catch (Exception e) {
            return "获取堆栈信息失败：" + e.getMessage();
        }
    }
}